package com.cqan.dao.mongo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

import org.springframework.beans.factory.annotation.Autowired;

import com.cqan.dao.BaseDao;
import com.cqan.orm.Page;
import com.cqan.orm.PropertyFilter;
import com.cqan.orm.PropertyFilter.MatchType;
import com.cqan.util.reflection.ReflectionUtils;
import com.google.code.morphia.Datastore;
import com.google.code.morphia.DatastoreImpl;
import com.google.code.morphia.Key;
import com.google.code.morphia.dao.DAO;
import com.google.code.morphia.query.Query;
import com.google.code.morphia.query.QueryResults;
import com.google.code.morphia.query.UpdateOperations;
import com.google.code.morphia.query.UpdateResults;
import com.mongodb.DBCollection;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;

/**
 * @Description mongodb持久化实现
 * @author slaton.wu@gmail.com
 * @date 2012-7-4下午1:44:19
 *
 */
public class MongodbDao<T, PK extends Serializable> implements BaseDao<T, PK>,DAO<T,PK> {
	private DatastoreImpl ds;
	protected Class<T> entityClass;
	
	public MongodbDao() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}
	
	/**
	 * Converts from a List<Key> to their id values
	 * 
	 * @param keys
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected List<?> keysToIds(List<Key<T>> keys) {
		ArrayList ids = new ArrayList(keys.size() * 2);
		for (Key<T> key : keys)
			ids.add(key.getId());
		return ids;
	}

	/** The underlying collection for this DAO */
	@Override
	public DBCollection getCollection() {
		return ds.getCollection(entityClass);
	}

	
	@Override
	public Query<T> createQuery() {
		return ds.createQuery(entityClass);
	}

	@Override
	public UpdateOperations<T> createUpdateOperations() {
		return ds.createUpdateOperations(entityClass);
	}

	@Override
	public Class<T> getEntityClass() {
		return entityClass;
	}

	
	@Override
	public Key<T> save(T entity) {
		return ds.save(entity);
	}

	
	@Override
	public Key<T> save(T entity, WriteConcern wc) {
		return ds.save(entity, wc);
	}

	
	@Override
	public UpdateResults<T> updateFirst(Query<T> q, UpdateOperations<T> ops) {
		return ds.updateFirst(q, ops);
	}

	
	@Override
	public UpdateResults<T> update(Query<T> q, UpdateOperations<T> ops) {
		return ds.update(q, ops);
	}

	
	@Override
	public WriteResult delete(T entity) {
		return ds.delete(entity);
	}

	@Override
	public WriteResult delete(T entity, WriteConcern wc) {
		return ds.delete(entity, wc);
	}

	@Override
	public WriteResult deleteById(PK id) {
		return ds.delete(entityClass, id);
	}

	@Override
	public WriteResult deleteByQuery(Query<T> q) {
		return ds.delete(q);
	}

	@Override
	public T get(PK id) {
		return ds.get(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findIds(String key, Object value) {
		return (List<T>) keysToIds(ds.find(entityClass, key, value).asKeyList());
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Key<T>> findIds() {
		return (List<Key<T>>) keysToIds(ds.find(entityClass).asKeyList());
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Key<T>> findIds(Query<T> q) {
		return (List<Key<T>>) keysToIds(q.asKeyList());
	}

	@Override
	public boolean exists(String key, Object value) {
		return exists(ds.find(entityClass, key, value));
	}

	@Override
	public boolean exists(Query<T> q) {
		return ds.getCount(q) > 0;
	}

	@Override
	public long count() {
		return ds.getCount(entityClass);
	}

	@Override
	public long count(String key, Object value) {
		return count(ds.find(entityClass, key, value));
	}

	@Override
	public long count(Query<T> q) {
		return ds.getCount(q);
	}

	@Override
	public T findOne(String key, Object value) {
		return ds.find(entityClass, key, value).get();
	}

	@Override
	public T findOne(Query<T> q) {
		return q.get();
	}

	@Override
	public QueryResults<T> find() {
		return createQuery();
	}

	@Override
	public QueryResults<T> find(Query<T> q) {
		return q;
	}

	@Override
	public Datastore getDatastore() {
		return ds;
	}

	@Override
	public void ensureIndexes() {
		ds.ensureIndexes(entityClass);
	}

	@Autowired
	public void setDs(DatastoreImpl ds) {
		this.ds = ds;
	}
	
	@Override
	@Deprecated
	public void saveEntity(T t) {
		
	}

	
	@Override
	public void delete(PK id) {
		ds.delete(get(id));
	}

	
	@Override
	public void delete(List<PK> ids) {
		
		ds.delete(ds.get(entityClass, ids));
	}

	
	@Override
	public Page<T> getAll(Page<T> page) {
		Query<T> query = ds.createQuery(entityClass);
		long totalCount = query.countAll();
		List<T> result = query.order(page.getOrder()).offset(page.getPageSize()*(page.getPageNo()-1)).limit(page.getPageSize()).asList();
		page.setTotalCount((int) totalCount);
		page.setResult(result);
		return page;
	}

	
	@Override
	public List<T> getAll() {
		
		return ds.createQuery(entityClass).asList();
	}

	
	@Override
	public List<T> find(List<PropertyFilter> filters) {
		Query<T> query = this.createQuery();
		if (!filters.isEmpty()) {
			for (PropertyFilter filter : filters) {
				query = queryFilter(filter.getPropertyName(),filter.getMatchValue(),filter.getMatchType(), query);
			}
		}
		return query.asList();
	}

	@Override
	public Page<T> findPage(Page<T> page, List<PropertyFilter> filters) {
		
			Query<T> query = this.createQuery();
			if (!filters.isEmpty()) {
				for (PropertyFilter filter : filters) {
					query = queryFilter(filter.getPropertyName(),filter.getMatchValue(),filter.getMatchType(), query);
				}
			}
			int totalCount = (int) query.countAll();
			 List<T> result = query.offset(page.getPageSize()*(page.getPageNo()-1)).limit(page.getPageSize()).asList();
			 page.setResult(result);
			 page.setTotalCount(totalCount);
			 return page;
	}

	
	@Override
	public List<T> findBy(String propertyName, Object value, MatchType matchType) {
		Query<T> query = this.createQuery();	
		query = queryFilter(propertyName, value, matchType, query);
		return query.asList();
	}

	/**
	 * @param propertyName
	 * @param value
	 * @param matchType
	 * @param query
	 * @return
	 */
	private Query<T> queryFilter(String propertyName, Object value,
			MatchType matchType, Query<T> query) {
		switch (matchType) {
			case EQ:
				query = query.field(propertyName).equal(value);
				break;
			case GT:
				query = query.filter(propertyName+" >", value);
				break;
			case LT:
				query = query.filter(propertyName+" <", value);
				break; 
			case GE:
				query = query.filter(propertyName+" >=", value);
				break; 
			case LE:
				query = query.filter(propertyName+" <=", value);
				break; 
			case LIKE:
				Pattern pattern = Pattern.compile("^" + value + ".*$", Pattern.CASE_INSENSITIVE);
				query = query.filter(propertyName,pattern);
				break; 
		}
		return query;
	}

	
	@Override
	public T findUniqueBy(String propertyName, Object value) {
		
		return ds.createQuery(entityClass).field(propertyName).contains(value.toString()).get();
	}

	
	@Override
	@Deprecated
	public void update(T t) {
		
	}

	
	@Override
	public Page<T> findPage(Page<T> page, PropertyFilter filter) {
		
		return null;
	}
	

	@Override
	public List<T> get(Collection<PK> ids) {
		// TODO Auto-generated method stub
		return null;
	}


}
